#ifndef __ATLOBJUTIL_H__
#define __ATLOBJUTIL_H__

#pragma once

#ifndef __cplusplus
	#error WTL requires C++ compilation (use a .cpp suffix)
#endif

namespace WTL
{

template <bool t_bManaged, HANDLE t_defaultType = NULL>
class CHandleT
{
public:
// Data members
	HANDLE m_hHandle;

// Constructor/destructor/operators
	CHandleT(HANDLE hHandle = t_defaultType) : m_hHandle(hHandle)
	{ }

	~CHandleT()
	{
		if(t_bManaged)
			CloseHandle();
	}

	CMenuT<t_bManaged>& operator =(HANDLE hHandle)
	{
		Attach(hHandle);
		return *this;
	}

	BOOL Attach(HANDLE hHandle)
	{
		if(t_bManaged && !IsNull && !IsInvalidHandleValue() && m_hHandle != hHandle)
				if(!CloseHandle())
						return FALSE;
		m_hHandle = hHandle;
		return TRUE;
	}

	HANDLE Detach()
	{
		HANDLE hHandle = m_hHandle;
		m_hHandle = t_defaultType;
		return hHandle;
	}

  operator HANDLE() const { return m_hHandle; }

	bool IsNull() const { return (NULL == m_hHandle); }

	bool IsInvalidHandleValue() const { return (INVALID_HANDLE_VALUE == m_hHandle); }

	BOOL CloseHandle()
	{
		if (IsNull() || IsInvalidHandleValue())
				return FALSE;
		
		BOOL bRet = ::CloseHandle(m_hHandle);			
		if(bRet)
			m_hHandle = t_defaultType;
		return bRet;		
	}

	#if (_WIN32_WINNT >= 0x0400)
	BOOL GetHandleInformation(LPDWORD lpdwFlags)
	{
		ATLASSERT(IsNull() || IsInvalidHandleValue());
		ATLASSERT(NULL == lpdwFlags)		
		return ::GetHandleInformation(m_hHandle, lpdwFlags);
	}

	BOOL SetHandleInformation(DWORD dwMask, DWORD dwFlags)
	{
		ATLASSERT(IsNull() || IsInvalidHandleValue());
		return ::SetHandleInformation(m_hHandle, dwMask, dwFlags);
	} 
#endif // (_WIN32_WINNT >= 0x0400)


	HANDLE DuplicateHandle(HANDLE hTargetProcessHandle = NULL, DWORD dwDesiredAccess = 0, BOOL bInheritHandle = FALSE, DWORD dwOptions = DUPLICATE_SAME_ACCESS) const
	{
		return DuplicateHandle(::GetCurrentProcess(), hTargetProcessHandle, dwDesiredAccess, bInheritHandle, dwOptions);
   }

	HANDLE DuplicateHandle(HANDLE hSourceProcessHandle = NULL, HANDLE hTargetProcessHandle = NULL, DWORD dwDesiredAccess = 0, BOOL bInheritHandle = FALSE, DWORD dwOptions = DUPLICATE_SAME_ACCESS) const
	{
		HANDLE hNewHandle = {t_defaultType};
		if (!IsNull() && !IsInvalidHandleValue())
		{
			if(!hSourceProcessHandle) {
				hSourceProcessHandle = ::GetCurrentProcess();
			}
			if(!hTargetProcessHandle) {
				hTargetProcessHandle = hSourceProcessHandle;
			}
			ATLVERIFY(::DuplicateHandle(hSourceProcessHandle, m_hHandle, hTargetProcessHandle, &t_defaultType, dwDesiredAccess, bInheritHandle, dwOptions) != FALSE);
			if (DUPLICATE_CLOSE_SOURCE == dwOptions & DUPLICATE_CLOSE_SOURCE)
			{
				m_hHandle = t_defaultType;
			}
		}
		return hNewHandle;
   }
};
typedef CHandleT<true> CHandle;
typedef CHandleT<false> CUnmgHandle;

template <bool t_bManaged>
class CFileMappingT: public CHandleT<t_bManaged, INVALID_HANDLE_VALUE>
{
private:
		typedef CHandleT<t_bManaged, INVALID_HANDLE_VALUE> baseClass;
		int			m_nMappingCounter;
public:
		LPVOID m_pData;
		CFileMappingT<t_bManaged>(HANDLE fileHandle = INVALID_HANDLE_VALUE, LPVOID data = NULL ) : baseClass(fileHandle), m_pData(data)
		{}
		~CFileMappingT<t_bManaged>()
		{
				if (t_bManaged && NULL != m_pData)
				{
						UnmapViewOfFile();
				}	
		}

	BOOL Attach(HANDLE hHandle)
	{	
	  BOOL bRet(FALSE);	
		if(t_bManaged && NULL != m_pData && m_hHandle != hHandle)
		{
				bRet = UnmapViewOfFile();
		}
		if (!baseClass::Attach(hHandle);)
		{
				return FALSE;
		}
		return bRet;
	}

	BOOL Attach(HANDLE hHandle, LPVOID pData)
	{		
		BOOL bRet = Attach(hHandle);
		m_pData = pData;
		return bRet;
	}

	HANDLE Detach()
	{
		HANDLE hHandle = m_hHandle;
		m_hHandle = INVALID_HANDLE_VALUE;
		return hHandle;
	}

	LPVOID DetachData()
	{
		LPVOID pData = m_pData;
		m_pData = NULL;
		return pData;
	}

		HANDLE CreateFileMapping(
				HANDLE hFile,
				DWORD flProtect, DWORD dwMaximumSizeHigh, DWORD dwMaximumSizeLow,
        LPCTSTR lpName = NULL, LPSECURITY_ATTRIBUTES lpFileMappingAttributes = NULL)
		{
				if (t_bManaged)
				{						
						UnmapViewOfFile();
						CloseHandle();
				}
				m_hHandle = ::CreateFileMapping(hFile, lpFileMappingAttributes, flProtect, dwMaximumSizeHigh, dwMaximumSizeLow, lpName);
				return m_hHandle;
		}

		HANDLE OpenFileMapping(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName)
		{
				if (t_bManaged)
				{						
						UnmapViewOfFile();
						CloseHandle();
				}
				m_hHandle = ::OpenFileMapping(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName);
				return m_hHandle;
		}

	LPVOID MapViewOfFile(
						 DWORD dwDesiredAccess, DWORD dwFileOffsetHigh, DWORD dwFileOffsetLow, SIZE_T dwNumberOfBytesToMap)
  {
    m_pData = ::MapViewOfFile(m_hHandle, dwDesiredAccess, dwFileOffsetHigh, dwFileOffsetLow, dwNumberOfBytesToMap);
		return m_pData;	
  }

	BOOL UnmapViewOfFile()
	{
			if (!m_pData)
			{
				return FALSE;
			}
			BOOL bRet = ::UnmapViewOfFile(m_pData);
			if (bRet)
			{
				m_pData = NULL;
			}
			return bRet;
	}

	BOOL FlushViewOfFile(SIZE_T dwNumberOfBytesToFlush)
	{
		return ::FlushViewOfFile(m_pData, dwNumberOfBytesToFlush);
	}


};

typedef CFileMappingT<true> CFileMapping;
typedef CFileMappingT<false> CFileMappingHandle;

}; //namespace WTL

#endif